import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

public class Utils {

	public static ArrayList<ArrayList<Sample>> division(ArrayList<Sample> samples, double trainingRatio) {
		int instances = samples.size();
		int nSamplesTraining = Math.round((float) (trainingRatio * instances));

		ArrayList<Sample> trainingSet = new ArrayList<Sample>(nSamplesTraining);
		ArrayList<Sample> testSet = new ArrayList<Sample>(instances - nSamplesTraining);
		ArrayList<ArrayList<Sample>> divisao = new ArrayList<ArrayList<Sample>>(2);
		divisao.add(trainingSet);
		divisao.add(testSet);

		for (int i = 0; i < nSamplesTraining; i++) {
			trainingSet.add(samples.get(i));
		}

		for (int i = nSamplesTraining; i < instances; i++) {
			testSet.add(samples.get(i));
		}

		return divisao;
	}

	public static ArrayList<ArrayList<Sample>> balancedDivision(DataBase dataBase, double trainingRatio) {
		ArrayList<Sample> samples = dataBase.getSamples();
		HashMap<String, ArrayList<Sample>> samplesByClass = dataBase.getSamplesByClass();

		int instances = samples.size();
		int nSamplesTraining = Math.round((float) (trainingRatio * instances));

		ArrayList<Sample> trainingSet = new ArrayList<Sample>(nSamplesTraining);
		ArrayList<Sample> testSet = new ArrayList<Sample>(instances - nSamplesTraining);
		ArrayList<ArrayList<Sample>> divisao = new ArrayList<ArrayList<Sample>>(2);
		divisao.add(trainingSet);
		divisao.add(testSet);

		Set<String> classes = samplesByClass.keySet();

		for (String classe : classes) {
			ArrayList<Sample> samplesClass = samplesByClass.get(classe);

			int instancesClass = samplesClass.size();
			int trainingSamples = Math.round((float) (trainingRatio * instancesClass));

			for (int i = 0; i < trainingSamples; i++) {
				trainingSet.add(samplesClass.get(i));
			}

			for (int i = trainingSamples; i < instancesClass; i++) {
				testSet.add(samplesClass.get(i));
			}
		}

		return divisao;
	}

	public static ArrayList<ArrayList<Sample>> division(ArrayList<Sample> samples, int nGroups) {
		int instances = samples.size();
		int samplesPerGroup = instances / nGroups;
		int rest = instances % nGroups;

		ArrayList<ArrayList<Sample>> divisao = new ArrayList<ArrayList<Sample>>(nGroups);

		for (int i = 0; i < nGroups; i++) {
			divisao.add(new ArrayList<Sample>(samplesPerGroup));
		}

		for (int i = 0; i < nGroups; i++) {
			for (int j = 0; j < samplesPerGroup; j++) {
				divisao.get(i).add(samples.get(i * samplesPerGroup + j));
			}
		}

		for (int i = 0; i < rest; i++) {
			divisao.get(i).add(samples.get(nGroups * samplesPerGroup + i));
		}

		return divisao;
	}

	public static ArrayList<ArrayList<Sample>> balancedDivision(DataBase dataBase, int nGroups) {
		ArrayList<Sample> samples = dataBase.getSamples();
		HashMap<String, ArrayList<Sample>> samplesByClass = dataBase.getSamplesByClass();

		int instances = samples.size();
		int samplesPerGroup = instances / nGroups;

		ArrayList<ArrayList<Sample>> divisao = new ArrayList<ArrayList<Sample>>(nGroups);

		for (int i = 0; i < nGroups; i++) {
			divisao.add(new ArrayList<Sample>(samplesPerGroup));
		}

		int i = 0;

		Set<String> classes = samplesByClass.keySet();

		for (String classe : classes) {
			ArrayList<Sample> samplesClass = samplesByClass.get(classe);

			for (Sample sample : samplesClass) {
				divisao.get(i++ % nGroups).add(sample);
			}
		}

		return divisao;
	}

	public static double crossValidation(DataBase dataBase, int n) throws Exception {
		ArrayList<ArrayList<Sample>> samplesGroups = Utils.balancedDivision(dataBase, n);
		int instances = dataBase.getSamples().size();

		double cv = 0;

		for (int i = 0; i < n; i++) {
			ArrayList<Sample> testSet = samplesGroups.get(i);
			ArrayList<Sample> trainingSet = new ArrayList<Sample>(instances - testSet.size());

			for (int j = 0; j < n; j++) {
				if (i != j) {
					trainingSet.addAll(samplesGroups.get(j));
				}
			}

			NearestCentroidClassifier nearestCentroid = new NearestCentroidClassifier(trainingSet, testSet);

			cv += nearestCentroid.classifyTestSet();
		}

		return cv / n;
	}
}
